package cn.edu.wfit.conf;


import cn.edu.wfit.entity.JsonResult;
import cn.edu.wfit.entity.Users;
import cn.edu.wfit.service.JwtService;
import cn.edu.wfit.service.LoginUser;
import cn.edu.wfit.service.RedisCache;
import com.alibaba.fastjson.JSON;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Configuration
@EnableWebSecurity // 开启WebSecurity相关功能
@EnableMethodSecurity // 开启方法安全校验
public class SecurityConfig {
    @Autowired
    private JwtSecurityFilter jwtSecurityFilter;
    @Autowired
    private JwtService jwtService;
    @Autowired
    private RedisCache redisCache;
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
        return http
                // 以下是验证请求拦截和放行配置
                .authorizeHttpRequests(auth -> {
                    auth.antMatchers("/test").permitAll(); // 不拦截指定的页面
                    auth.antMatchers("/index").hasAnyRole("user","admin");
//                    auth.anyRequest().hasRole("admin");
                    auth.anyRequest().authenticated(); // 将所有请求全部拦截，一律需要验证
                })
                // 以下是表单登录相关配置
                .formLogin(conf->{
                    conf.successHandler(this::onAuthenticationSuccess);// 登录成功执行的方法
                    conf.failureHandler(this::onAuthenticationFailure);// 登录失败执行的方法
                    conf.loginProcessingUrl("/dologin");// 表单提交的地址，可以自定义
                    conf.permitAll();//将登录相关的地址放行，否则未登录的用户连登录界面都进不去
                    //用户名和密码的表单字段名称，不过默认就是这个，可以不配置，除非有特殊需求
                    conf.usernameParameter("username");
                    conf.passwordParameter("password");
                })
                .sessionManagement(config->config.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) // 禁用session
                .headers(conf->{
                    conf.frameOptions().disable();
                    conf.cacheControl().disable(); // 禁用缓存
                })
                .exceptionHandling(config->config
                        //已登录但没有权限的情况下访问需要角色权限的接口
                        .accessDeniedHandler(this::onAccessDeniedHandler)
                        //没有登录的情况下访问需要登录的接口
                        .authenticationEntryPoint(this::commence))
                .cors(config->config.configurationSource(this.corsConfigurationSource()))
                .csrf(AbstractHttpConfigurer::disable) // 禁用csrf
                .addFilterBefore(jwtSecurityFilter, UsernamePasswordAuthenticationFilter.class)
                .build();
    }
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration cors = new CorsConfiguration();
        cors.addAllowedOrigin("*");
        cors.addAllowedHeader("*");
        cors.addAllowedMethod("*");
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**",cors);
        return source;
    }
    // 登录失败
    void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException {
        response.setContentType("application/json;charset=UTF-8");
        exception.printStackTrace();
        String str = JSON.toJSONString(JsonResult.failure(400,exception.getMessage()));
        response.getWriter().write(str);
    }
    // 登录成功
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        LoginUser principal = (LoginUser)authentication.getPrincipal();
        Users users = principal.getUser();
        // 将用户信息存入redis中，用户id作为键
        redisCache.setCacheObject(users.getId().toString(), principal);
        String token = jwtService.generateToken(users.getId().toString());
        response.setContentType("application/json;charset=UTF-8");
        String str = JSON.toJSONString(JsonResult.ok(token));
        response.getWriter().write(str);
    }

    public void onAccessDeniedHandler(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {
        response.setContentType("application/json;charset=UTF-8");
        accessDeniedException.printStackTrace();
        String str = JSON.toJSONString(JsonResult.failure(403,accessDeniedException.getMessage()));
        response.getWriter().write(str);
    }

    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
        response.setContentType("application/json;charset=UTF-8");
//        authException.printStackTrace();
        String str = JSON.toJSONString(JsonResult.failure(400,authException.getMessage()));
        response.getWriter().write(str);
    }
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
}